package br.com.ac.fatureweb.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.FlushModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Root;

import br.com.ac.fatureweb.enums.EnumStatusCarga;
import br.com.ac.fatureweb.integration.billing.ContaVO;
import br.com.ac.fatureweb.integration.billing.LancamentoCobradoVO;
import br.com.ac.fatureweb.integration.billing.LogCorrecaoContaVO;
import br.com.ac.fatureweb.integration.billing.LogLeituraVO;
import br.com.ac.fatureweb.integration.billing.ProdutoVO;
import br.com.ac.fatureweb.integration.billing.ValorVO;
import br.com.ac.fatureweb.integration.billing.VolumeVO;
import br.com.ac.fatureweb.model.Roteiro;
import br.com.ac.fatureweb.model.billing.Conta;
import br.com.ac.fatureweb.model.billing.LancamentoCobrado;
import br.com.ac.fatureweb.model.billing.LogCorrecaoConta;
import br.com.ac.fatureweb.model.billing.LogLeitura;
import br.com.ac.fatureweb.model.billing.Produto;
import br.com.ac.fatureweb.model.billing.Valor;
import br.com.ac.fatureweb.model.billing.Volume;

@Local
@Named
@Stateless
public class NgcExportacaoBean {

	@PersistenceContext
	private EntityManager entityManager;

	@SuppressWarnings("unchecked")
	public String getCodLeituristaSaae(Long idLeiturista) {
		Query query = entityManager.createQuery("SELECT leiSa.codigo "
				+ " FROM Leiturista lei INNER JOIN lei.leituristaSaae leiSa "
				+ " WHERE lei.id =:id");

		query.setParameter("id", idLeiturista);

		List<Long> resultList = query.getResultList();

		if (resultList != null && !resultList.isEmpty()) {
			return resultList.iterator().next().toString();
		}

		return "";
	}

	@SuppressWarnings("unchecked")
	public List<LogLeitura> getLstLeituras(Long id) {
		Query query = entityManager
				.createQuery("SELECT log FROM LogLeitura log "
						+ " WHERE log.idCarga =:idCarga "
						+ " AND log.enumStatus <> :enumStatus");

		query.setParameter("idCarga", id);

		query.setParameter("enumStatus", EnumStatusCarga.ENVIADO_SAAE);

		List<LogLeitura> resultList = query.getResultList();

		return resultList;
	}

	/**
	 * Gravar os dados de faturamento recebidos dos equipamentos.
	 * 
	 * @param idRoteiro
	 * 
	 * @param lstConta
	 * @param lstLancamento
	 * @param lstLogCorrecao
	 * @param lstLogLeitura
	 * @param lstProduto
	 * @param lstValor
	 * @param lstVolume
	 */
	public void receiveRoute(Long idRoteiro, List<ContaVO> lstConta,
			List<LancamentoCobradoVO> lstLancamento,
			List<LogCorrecaoContaVO> lstLogCorrecao,
			List<LogLeituraVO> lstLogLeitura, List<ProdutoVO> lstProduto,
			List<ValorVO> lstValor, List<VolumeVO> lstVolume) {

		// comitar tudo de uma vez só...
		entityManager.setFlushMode(FlushModeType.COMMIT);

		List<Long> lstInscricaoCorrecao = new ArrayList<Long>();

		if (lstLogCorrecao != null && !lstLogCorrecao.isEmpty()) {
			for (LogCorrecaoContaVO logCorrecaoContaVO : lstLogCorrecao) {

				LogCorrecaoConta logCorrecaoConta = new LogCorrecaoConta(
						logCorrecaoContaVO, idRoteiro);

				Long inscricao = logCorrecaoContaVO.getInscricao();

				if (!lstInscricaoCorrecao.contains(inscricao)) {
					excluirDadosJaRetornados(logCorrecaoContaVO, idRoteiro);
				}

				lstInscricaoCorrecao.add(inscricao);

				entityManager.persist(logCorrecaoConta);
			}
		}

		entityManager.flush();

		CriteriaBuilder cb = entityManager.getCriteriaBuilder();

		List<Conta> lstContaTemRetorno = new ArrayList<Conta>();

		if (lstConta != null && !lstConta.isEmpty()) {
			for (ContaVO contaVO : lstConta) {
				lstContaTemRetorno.addAll(getLstConta(idRoteiro, cb,
						contaVO.getInscricao()));
			}
		}

		if (lstConta != null && !lstConta.isEmpty()) {
			for (ContaVO contaVO : lstConta) {
				Conta conta = new Conta(contaVO, idRoteiro);
				boolean contains = isContemInscricao(lstContaTemRetorno,
						conta.getInscricao());
				if (!contains) {
					entityManager.persist(conta);
				}
			}
		}

		if (lstLancamento != null && !lstLancamento.isEmpty()) {
			for (LancamentoCobradoVO lancamentoCobradoVO : lstLancamento) {
				LancamentoCobrado lancamentoCobrado = new LancamentoCobrado(
						lancamentoCobradoVO, idRoteiro);
				boolean contains = isContemInscricao(lstContaTemRetorno,
						lancamentoCobrado.getInscricao());
				if (!contains) {
					entityManager.persist(lancamentoCobrado);
				}
			}
		}

		// Atualizar o roteiro com a maior localiza��o geogr�fica.
		Double longitude = null;
		Double latitude = null;
		Date dataLeitura = null;

		if (lstLogLeitura != null && !lstLogLeitura.isEmpty()) {
			for (LogLeituraVO logLeituraVO : lstLogLeitura) {
				LogLeitura logLeitura = new LogLeitura(logLeituraVO, idRoteiro);
				if (logLeitura.getObservacao().contains("'")) {
					logLeitura.setObservacao(null);
				}

				if (dataLeitura != null
						&& logLeitura.getDataLeitura() != null
						&& dataLeitura.getTime() > logLeitura.getDataLeitura()
								.getTime()) {

					if (logLeitura.getLatitude() != null
							&& logLeitura.getLongitude() != null) {
						latitude = logLeitura.getLatitude();
						longitude = logLeitura.getLongitude();
					}
				}

				dataLeitura = logLeitura.getDataLeitura();

				boolean contains = isContemInscricao(lstContaTemRetorno,
						logLeitura.getInscricao());
				if (!contains) {
					logLeitura.setEnumStatus(EnumStatusCarga.FINALIZADO);
					entityManager.persist(logLeitura);
				}
			}
		}

		if (idRoteiro != null && latitude != null && longitude != null) {

			Roteiro reference = entityManager.getReference(Roteiro.class,
					idRoteiro);

			reference.setLatitude(latitude);
			reference.setLongitude(longitude);

			entityManager.merge(reference);
		}

		if (lstProduto != null && !lstProduto.isEmpty()) {
			for (ProdutoVO produtoVO : lstProduto) {
				Produto produto = new Produto(produtoVO, idRoteiro);
				boolean contains = isContemInscricao(lstContaTemRetorno,
						produto.getInscricao());
				if (!contains) {
					entityManager.persist(produto);
				}
			}
		}

		if (lstValor != null && !lstValor.isEmpty()) {
			for (ValorVO valorVO : lstValor) {
				Valor valor = new Valor(valorVO, idRoteiro);
				boolean contains = isContemInscricao(lstContaTemRetorno,
						valor.getInscricao());
				if (!contains) {
					entityManager.persist(valor);
				}
			}
		}

		if (lstVolume != null && !lstVolume.isEmpty()) {
			for (VolumeVO volumeVO : lstVolume) {
				Volume volume = new Volume(volumeVO, idRoteiro);
				boolean contains = isContemInscricao(lstContaTemRetorno,
						volume.getInscricao());
				if (!contains) {
					entityManager.persist(volume);
				}
			}
		}
	}

	private boolean isContemInscricao(List<Conta> lstContaTemRetorno,
			Long inscricao) {
		if (lstContaTemRetorno != null && !lstContaTemRetorno.isEmpty()
				&& inscricao != null) {
			for (Conta conta : lstContaTemRetorno) {
				if (conta.getInscricao() != null
						&& conta.getInscricao().equals(inscricao)) {
					return true;
				}
			}
		}
		return false;
	}

	private void excluirDadosJaRetornados(
			LogCorrecaoContaVO logCorrecaoContaVO, Long idRoteiro) {

		CriteriaBuilder cb = entityManager.getCriteriaBuilder();

		List<Conta> lstConta = getLstConta(idRoteiro, cb,
				logCorrecaoContaVO.getInscricao());
		if (lstConta != null && !lstConta.isEmpty()) {
			for (Conta conta : lstConta) {
				Conta reference = entityManager.getReference(Conta.class,
						conta.getSeq());

				entityManager.remove(reference);
			}
		}

		List<LancamentoCobrado> lstLancamento = getLstLancamento(idRoteiro, cb,
				logCorrecaoContaVO.getInscricao());
		if (lstLancamento != null && !lstLancamento.isEmpty()) {
			for (LancamentoCobrado lancamentoCobrado : lstLancamento) {
				LancamentoCobrado reference = entityManager.getReference(
						LancamentoCobrado.class, lancamentoCobrado.getSeq());

				entityManager.remove(reference);
			}
		}

		List<LogCorrecaoConta> lstLogCorrecao = getLstLogCorrecao(idRoteiro,
				cb, logCorrecaoContaVO.getInscricao());
		if (lstLogCorrecao != null && !lstLogCorrecao.isEmpty()) {
			for (LogCorrecaoConta logCorrecaoConta : lstLogCorrecao) {
				LogCorrecaoConta reference = entityManager.getReference(
						LogCorrecaoConta.class, logCorrecaoConta.getSeq());

				entityManager.remove(reference);
			}
		}

		List<LogLeitura> lstLogLeitura = getLstLogLeitura(idRoteiro, cb,
				logCorrecaoContaVO.getInscricao());
		if (lstLogLeitura != null && !lstLogLeitura.isEmpty()) {
			for (LogLeitura logLeitura : lstLogLeitura) {
				LogLeitura reference = entityManager.getReference(
						LogLeitura.class, logLeitura.getSeq());

				entityManager.remove(reference);
			}
		}

		List<Produto> lstProduto = getLstProduto(idRoteiro, cb,
				logCorrecaoContaVO.getInscricao());
		if (lstProduto != null && !lstProduto.isEmpty()) {
			for (Produto produto : lstProduto) {
				Produto reference = entityManager.getReference(Produto.class,
						produto.getSeq());

				entityManager.remove(reference);
			}
		}

		List<Valor> lstValor = getLstValor(idRoteiro, cb,
				logCorrecaoContaVO.getInscricao());
		if (lstValor != null && !lstValor.isEmpty()) {
			for (Valor valor : lstValor) {
				Valor reference = entityManager.getReference(Valor.class,
						valor.getSeq());

				entityManager.remove(reference);
			}
		}

		List<Volume> lstVolume = getLstVolume(idRoteiro, cb,
				logCorrecaoContaVO.getInscricao());
		if (lstVolume != null && !lstVolume.isEmpty()) {
			for (Volume volume : lstVolume) {
				Volume reference = entityManager.getReference(Volume.class,
						volume.getId());

				entityManager.remove(reference);
			}
		}
	}

	private List<Volume> getLstVolume(Long idRoteiro, CriteriaBuilder cb,
			Long inscricao) {

		CriteriaQuery<Volume> cbVolume = cb.createQuery(Volume.class);

		Root<Volume> volume = cbVolume.from(Volume.class);

		Join<Volume, Roteiro> roteiro = volume.join("roteiro");

		cbVolume.where(cb.and(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.equal(volume.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<Volume> query = entityManager.createQuery(cbVolume);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<Volume> resultList = query.getResultList();

		return resultList;
	}

	private List<Valor> getLstValor(Long idRoteiro, CriteriaBuilder cb,
			Long inscricao) {

		CriteriaQuery<Valor> cbValor = cb.createQuery(Valor.class);

		Root<Valor> valor = cbValor.from(Valor.class);

		Join<Valor, Roteiro> roteiro = valor.join("roteiro");

		cbValor.where(cb.and(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.equal(valor.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<Valor> query = entityManager.createQuery(cbValor);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<Valor> resultList = query.getResultList();

		return resultList;
	}

	private List<Produto> getLstProduto(Long idRoteiro, CriteriaBuilder cb,
			Long inscricao) {

		CriteriaQuery<Produto> cbProduto = cb.createQuery(Produto.class);

		Root<Produto> produto = cbProduto.from(Produto.class);

		Join<Produto, Roteiro> roteiro = produto.join("roteiro");

		cbProduto.where(cb.and(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.equal(produto.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<Produto> query = entityManager.createQuery(cbProduto);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<Produto> resultList = query.getResultList();

		return resultList;
	}

	private List<LogLeitura> getLstLogLeitura(Long idRoteiro,
			CriteriaBuilder cb, Long inscricao) {

		CriteriaQuery<LogLeitura> cbLogLeitura = cb
				.createQuery(LogLeitura.class);

		Root<LogLeitura> logLeitura = cbLogLeitura.from(LogLeitura.class);

		Join<LogLeitura, Roteiro> roteiro = logLeitura.join("roteiro");

		cbLogLeitura.where(cb.and(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.equal(logLeitura.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<LogLeitura> query = entityManager.createQuery(cbLogLeitura);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<LogLeitura> resultList = query.getResultList();

		return resultList;
	}

	private List<LogCorrecaoConta> getLstLogCorrecao(Long idRoteiro,
			CriteriaBuilder cb, Long inscricao) {

		CriteriaQuery<LogCorrecaoConta> cbLogCorrecaoConta = cb
				.createQuery(LogCorrecaoConta.class);

		Root<LogCorrecaoConta> logCorrecaoConta = cbLogCorrecaoConta
				.from(LogCorrecaoConta.class);

		Join<LogCorrecaoConta, Roteiro> roteiro = logCorrecaoConta
				.join("roteiro");

		cbLogCorrecaoConta.where(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.and(cb.equal(logCorrecaoConta.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<LogCorrecaoConta> query = entityManager
				.createQuery(cbLogCorrecaoConta);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<LogCorrecaoConta> resultList = query.getResultList();

		return resultList;
	}

	private List<LancamentoCobrado> getLstLancamento(Long idRoteiro,
			CriteriaBuilder cb, Long inscricao) {

		CriteriaQuery<LancamentoCobrado> cbLancamentoCobrado = cb
				.createQuery(LancamentoCobrado.class);

		Root<LancamentoCobrado> lancamentoCobrado = cbLancamentoCobrado
				.from(LancamentoCobrado.class);

		Join<LancamentoCobrado, Roteiro> roteiro = lancamentoCobrado
				.join("roteiro");

		cbLancamentoCobrado.where(cb.and(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.equal(lancamentoCobrado.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<LancamentoCobrado> query = entityManager
				.createQuery(cbLancamentoCobrado);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<LancamentoCobrado> resultList = query.getResultList();

		return resultList;
	}

	private List<Conta> getLstConta(Long idRoteiro, CriteriaBuilder cb,
			Long inscricao) {

		CriteriaQuery<Conta> cbConta = cb.createQuery(Conta.class);

		Root<Conta> conta = cbConta.from(Conta.class);

		Join<Conta, Roteiro> roteiro = conta.join("roteiro");

		cbConta.where(cb.and(
				cb.equal(roteiro.get("id"), cb.parameter(Long.class, "id")),
				cb.equal(conta.get("inscricao"),
						cb.parameter(Long.class, "inscricao"))));

		TypedQuery<Conta> query = entityManager.createQuery(cbConta);

		query.setParameter("id", idRoteiro);

		query.setParameter("inscricao", inscricao);

		List<Conta> resultList = query.getResultList();

		return resultList;
	}
}
